package org.jackysoft.service;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
import org.jackysoft.data.AbstractNoSqlDataProvider;
import org.jackysoft.data.SortInfo;
import org.jackysoft.entity.nosql.AbstractNoSqlEntity;
import org.jackysoft.query.Query;
import org.jackysoft.util.FileInfo;
import org.jackysoft.util.OperationType;
import org.jackysoft.util.Page;
import org.springframework.dao.DataAccessException;

import com.google.common.collect.Lists;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;

public abstract class AbstractNoSqlServiceImpl<T extends AbstractNoSqlEntity>
		extends AbstractNoSqlDataProvider<T> {



	/**
	 * 
	 */
	private static final long serialVersionUID = -2405006268707541497L;
	/**
	 * 
	 */
	private static final Log logger = LogFactory
			.getLog(AbstractNoSqlServiceImpl.class);
	protected DBCollection collection;

	protected AbstractNoSqlServiceImpl() {
	}

	public AbstractNoSqlServiceImpl(Class<?> type) {
		this.type = type;

	}

	protected T find(String id) {
		DBObject obj = getCollection().findOne(
				new BasicDBObject().append("_id", new ObjectId(id)));
		T t = this.fromMongo2T(obj);
		return t;
	}

	protected List<T> findAll() {

		Collection<DBObject> dc = getCollection().find().toArray();

		return fromMongodbList(dc);
	}

	protected List<T> findAll(DBObject query) {
		Collection<DBObject> dc = collection.find(query).toArray();
		return fromMongodbList(dc);
	}

	protected List<T> findAll(int start, int size) {
		List<DBObject> list = getCollection().find().skip(start).limit(size)
				.toArray();
		return fromMongodbList(list);
	}

	protected List<T> findAll(int start, int size, DBObject query) {
		List<DBObject> list = collection.find(query)
				.sort(new BasicDBObject("createDate", -1)).skip(start)
				.limit(size).toArray();
		return fromMongodbList(list);
	}

	public void remove(T obj) {
		String sid = obj.getId();
		DBObject mdb = new BasicDBObject();
		mdb.put("_id", new ObjectId(sid));
		collection.remove(mdb);
	}

	@Override
	public void drop() {
		collection.drop();

	}

	protected void save(T obj) {
		DBObject dbo = obj.getRoot();

		WriteResult wr = collection.insert(dbo);
		logger.info(wr.getN() + " inserted of " + getType().getName()
				+ " with id of " + dbo.get("_id"));

	}

	protected void batchSave(List<T> objs) {
		getCollection().insert(this.fromTList(objs));
	}

	@Override
	public void update(T obj) {
		DBObject q = BasicDBObjectBuilder.start()
				.add("_id", new ObjectId(obj.getId())).get();
		DBObject val = obj.getRoot();
		val.removeField("_id");
		DBObject upvalue = new BasicDBObject("$set", val);
		collection.update(q, upvalue);
	}

	@Override
	protected int listDataSize(SortInfo sortInfo, Map<String, Object> smap) {
		DBObject query = parseSearchMap(smap);
		return this.count(query);
	}

	@Override
	protected List<T> listData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		DBObject query = parseSearchMap(smap);
		return findAll(start, size, query);
	}

	@Override
	protected List<T> searchData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {

		return null;
	}

	@Override
	protected int searchDataSize(SortInfo sortInfo, Map<String, Object> smap) {
		return 0;
	}

	@Override
	public T get(String id) throws DataAccessException {
		ObjectId oid = new ObjectId(id.toString());
		DBObject obj = collection.findOne(oid);

		return this.fromMongo2T(obj);
	}

	@Override
	public T get(DBObject query) throws DataAccessException {
		return fromMongo2T(collection.findOne(query));
	}

	@Override
	public Collection<T> gets(int start, int size) throws DataAccessException {
		return this.findAll(start, size);
	}

	@Override
	public Collection<T> gets(int start, int size, DBObject sort)
			throws DataAccessException {
		List<DBObject> list = collection.find().skip(start).limit(size)
				.sort(sort).toArray();
		return this.fromMongodbList(list);
	}

	@Override
	public Collection<T> gets() throws DataAccessException {
		return this.findAll();
	}

	@Override
	public void create(T b) throws DataAccessException {
		this.save(b);
	}

	@Override
	public void create(Collection<T> bs) throws DataAccessException {
		List<DBObject> list = Lists.newArrayList();
		list.addAll(this.fromTCollection((bs)));
		collection.insert(list);
	}

	@Override
	public void remove(Collection<T> bs) throws DataAccessException {
		for (T o : bs) {
			if (o == null)
				continue;
			collection.remove(o.getRoot());
		}
	}

	@Override
	public void update(Collection<T> bs) throws DataAccessException {
		for (T o : bs) {
			this.update(o);
		}
	}

	@Override
	public void saveOrUpdate(T b) throws DataAccessException {
		this.save(b);

	}

	@Override
	public int count() throws DataAccessException {
		return 0;
	}

	@Override
	public Iterable<T> getData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		if (smap.containsKey("_search"))
			return this.searchData(start, size, listAll, sortInfo, smap);
		return listData(start, size, listAll, sortInfo, smap);
	}

	@Override
	public int getSize(SortInfo sortInfo, Map<String, Object> smap) {
		if (smap.containsKey("_search"))
			return this.searchDataSize(sortInfo, smap);
		return listDataSize(sortInfo, smap);
	}

	@Override
	public void sorted(Collection<T> beans) throws Exception {

		for (T bean : beans) {
			ObjectId id = new ObjectId(bean.getId() + "");
			DBObject cons = new BasicDBObject("_id", id);
			DBObject sdata = new BasicDBObject("$set", new BasicDBObject(
					"sort", bean.getSort()));
			collection.update(cons, sdata);
		}

	}

	@Override
	public void detach() {
		// TODO Auto-generated method stub

	}

	@Override
	public void sorts(Collection<T> beans) {
	     
	}

	@Override
	public Page query(Query qc, Integer start, Integer rows)
			throws DataAccessException {
		List<DBObject> lists = collection.find(qc.mongoQuery()).skip(start)
				.limit(rows).toArray();
		Page page = new Page();
		page.setPage(0);
		Long count = collection.count(qc.mongoQuery());
		page.setRecords(count.intValue());
		page.setPage(start / rows);
		page.setRows(lists);
		return page;
	}

	
	
	
	@Override
	public Page details(Query qc, Integer pindex, Integer rows)
			throws DataAccessException {
		return this.query(qc, pindex, rows);
	}

	@Override
	public Collection<T> query(int start, int size, DBObject query)
			throws DataAccessException {
		List<DBObject> list = collection.find(query).skip(start).limit(size)
				.toArray();
		return this.fromMongodbList(list);
	}

	@Override
	public Collection<T> query(int start, int size, DBObject query,
			DBObject sort) throws DataAccessException {

		List<DBObject> list = collection.find(query).sort(sort).skip(start*size)
				.limit(size).toArray();
		return this.fromMongodbList(list);

	}

	@Override
	public int count(Query qc) throws DataAccessException {
		Long count = collection.count(qc.mongoQuery());
		return count.intValue();
	}

	@Resource
	public void setDb(DB db) {
		collection = db.getCollection(type.getName());
	}

	public DBCollection getCollection() {
		return collection;
	}

	@Override
	public void remove(String id) throws DataAccessException {
		DBObject qu = new BasicDBObject("_id", new ObjectId(id));
		collection.remove(qu);

	}

	@Override
	public void removeAll() throws DataAccessException {
		this.collection.drop();

	}

	@Override
	public Collection<T> gets(DBObject query) throws DataAccessException {
		List<DBObject> list = collection.find(query).toArray();
		return fromMongodbList(list);
	}

	@Override
	public Collection<T> getsAndSort(DBObject query, DBObject sort)
			throws DataAccessException {
		List<DBObject> list = collection.find(query).sort(sort).toArray();
		return fromMongodbList(list);
	}

	@Override
	public int count(DBObject query) {
		Long c = collection.count(query);

		return c.intValue();
	}

	protected List<DBObject> fromTCollection(Collection<T> col) {
		List<DBObject> colt = Lists.newArrayList();
		for (T dbo : col) {
			colt.add(dbo.getRoot());
		}
		return colt;
	}

	protected List<DBObject> fromTList(List<T> col) {
		List<DBObject> colt = Lists.newArrayList();
		for (T dbo : col) {
			colt.add(dbo.getRoot());
		}
		return colt;
	}

	protected List<T> fromMongodbList(Collection<DBObject> col) {
		List<T> colt = Lists.newArrayList();
		for (DBObject dbo : col) {
			colt.add(fromMongo2T(dbo));
		}
		return colt;
	}

	DBObject parseSearchMap(Map<String, Object> smap) {

		DBObject obj = new BasicDBObject();
		if (smap == null)
			return obj;
		smap.remove("operation");

		for (Map.Entry<String, Object> sentry : smap.entrySet()) {
			String[] vs = (String[]) sentry.getValue();
			obj.put(sentry.getKey(), vs[0]);
		}
		return obj;
	}

	/**
	 * 把原生的Mongodb转换成业务Bean
	 * */
	@SuppressWarnings("unchecked")
	protected T fromMongo2T(DBObject obj) {

		Object t = null;
		try {
			t = this.getType().newInstance();
			for (String key : obj.keySet()) {
				String name = key;
				Object objval = obj.get(key);
				if ("_class".equals(name) || objval instanceof BasicDBObject)
					continue;

				if ("_id".equals(name)) {
					name = "objectId";
					PropertyUtils.setProperty(t, name,
							new ObjectId(objval.toString()));
					continue;
				}

				Field f = this.getType().getDeclaredField(name);
				if (f.getType().isAssignableFrom(OperationType.class)) {
					Integer i = Integer.parseInt(objval.toString());
					objval = OperationType.lookup(i);
				}

				PropertyUtils.setProperty(t, name, objval);
			}
		} catch (InstantiationException e) {
			logger.error(e);
		} catch (IllegalAccessException e) {
			logger.error(e);
		} catch (InvocationTargetException e) {
			logger.error(e);

		} catch (NoSuchMethodException e) {
			logger.error(e);

		} catch (SecurityException e) {
			logger.error(e);
		} catch (NoSuchFieldException e) {
			logger.error(e);
		} finally{
			
		}
		return (T) t;

	}
	
	
	
	
	@Override
	public void create(File file, String name, T bean) throws Exception {
		// TODO Auto-generated method stub
		
	}

	@Override
	public FileInfo download(String id) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Collection<T> batchImport(byte[] data) {
		// TODO Auto-generated method stub
		return null;
	}

	
}